package com.cms.plugin.hook;

import com.cms.common.context.ApplicationContextProvider;
import com.cms.common.hook.target.BootstrapTarget;
import com.cms.common.hook.target.Hook;
import com.cms.common.hook.target.PluginInitTarget;
import com.cms.common.model.entity.Plugin;
import com.cms.common.hook.vo.HookParams;
import com.cms.common.hook.vo.HookPluginObject;
import com.cms.common.hook.vo.HookResult;
import com.gitee.starblues.core.PluginInfo;
import com.gitee.starblues.integration.user.PluginUser;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class HookServices {
    //Map to store the hook objects
    public static Map<String, List<HookPluginObject>> hookMap = new HashMap<>();//启去时检测好需要在启动时就注册好，这里直接使用
    //Map to store the plugins
    public static Map<String, Plugin> pluginsMap = new HashMap<>();


    //Register a hook
    public static HookResult regHook(String hookKey, HookParams params) {

        HookResult hookResult = null;
        //Check if the hook key exists in the hook map
        if (hookMap.containsKey(hookKey)) {
            //Add the hook key to the request parameters
            params.getRequestParams().put("hook_key", hookKey);

            //Get the hook plugin objects from the hook map
            List<HookPluginObject> hookPluginObjects = hookMap.get(hookKey);
            //If the hook plugin objects are null or empty, return a new HookResult
            if (hookPluginObjects == null || hookPluginObjects.size() == 0) {
                return new HookResult();
            }
            //Loop through the hook plugin objects
            for (HookPluginObject hookPluginObject : hookPluginObjects) {

                //Add the plugin config to the request parameters
                params.getRequestParams().put("plugin_config", hookPluginObject.getConfig());

                //Get the plugin user bean from the application context
                PluginUser pluginUser = ApplicationContextProvider.getBean(PluginUser.class);
                //Get the bean from the plugin user
                Object obj = pluginUser.getBean(hookPluginObject.getPluginId(), hookPluginObject.getClazz());
                //If the bean is null, continue to the next hook plugin object
                if (obj == null) {
                    continue;
                }
                //Get the class of the bean
                Class clazz = obj.getClass();
                //Get the methods of the class
                Method[] methods = clazz.getMethods();
                //Loop through the methods
                for (Method method : methods) {
                    //Get the hook annotation from the method
                    Hook hook = method.getAnnotation(Hook.class);
                    //If the hook annotation is not null
                    if (hook != null) {
                        //Get the hook key from the annotation
                        String key = hook.hook_key();
                        //If the hook key matches the hook key parameter
                        if (hookKey.equals(key)) {
                            try {
                                //Invoke the method with the parameters
                                hookResult = (HookResult) method.invoke(clazz.newInstance(), params);
                                //If the code is not 0, break the loop
                                if (hookResult.getCode() != 0) {
                                    break;
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            break;
                        }
                    }

                }
                //If the hook result is not null and the code is not 0, break the loop
                if (hookResult != null && hookResult.getCode() != 0) {
                    break;
                }
            }
        }
        //Return the hook result or a new HookResult
        return hookResult == null ? new HookResult() : hookResult;
    }

    //Convert the first character of a string to lowercase
    private static String lowerFirst(String str) {
        char[] cs = str.toCharArray();
        cs[0] += 32;
        return String.valueOf(cs);
    }

    //Initialize the hook map
    public static void initHookMap(PluginInfo pluginInfo) {
        //Get the plugin ID from the plugin info
        String pluginID = pluginInfo.getPluginDescriptor().getPluginId();
        //Get the plugin user bean from the application context
        PluginUser pluginUser = ApplicationContextProvider.getBean(PluginUser.class);
        //Get the beans with the BootstrapTarget annotation from the plugin user
        List<Object> objs = pluginUser.getBeansWithAnnotation(pluginID, BootstrapTarget.class);
        //Loop through the objects
        for (Object obj : objs) {
            //Get the class of the object
            Class<?> clazz = obj.getClass();
            //Get the methods of the class
            Method[] ms = clazz.getMethods();
            //Loop through the methods
            for (Method method : ms) {
                //If the method has the Hook annotation
                if (method.getAnnotation(Hook.class) != null) {
                    //Get the hook annotation from the method
                    Hook hook = method.getAnnotation(Hook.class);
                    //Get the hook key from the annotation
                    String key = hook.hook_key();
                    //Get the hook plugin objects from the hook map
                    List<HookPluginObject> hookPluginObjects = HookServices.hookMap.get(key);
                    //If the hook plugin objects are null, create a new list and add it to the hook map
                    if (hookPluginObjects == null) {
                        hookPluginObjects = new ArrayList<>();
                        HookServices.hookMap.put(key, hookPluginObjects);
                    }

                    //Get the name of the class
                    String name = clazz.getName();
                    //Get the last index of the dot in the name
                    name = name.substring(name.lastIndexOf(".") + 1);
                    //Convert the first character of the name to lowercase
                    name = lowerFirst(name);
                    //Create a new hook plugin object
                    HookPluginObject hookPluginObject = new HookPluginObject();
                    //Set the class name
                    hookPluginObject.setClazz(name);
                    //Set the method name
                    hookPluginObject.setMethod(method.getName());
                    //Set the plugin ID
                    hookPluginObject.setPluginId(pluginID);
                    //Set the sort by value
                    hookPluginObject.setSortBy(hook.sory_by());
                    //Add the hook plugin object to the list
                    hookPluginObjects.add(hookPluginObject);
                } else if (method.getAnnotation(PluginInitTarget.class) != null) {//初始化配置信息
                    try {
                        //Invoke the method to get the plugins
                        Plugin plugins = (Plugin) method.invoke(clazz.newInstance());
                        //Add the plugins to the plugins map
                        pluginsMap.put(pluginID, plugins);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

    //Delete the hook map
    public static void delHookMap(PluginInfo pluginInfo) {
        //Get the plugin ID from the plugin info
        String pluginID = pluginInfo.getPluginDescriptor().getPluginId();
        HookServices.hookMap.forEach((hook_key, hookPluginObjects) -> {
            List<HookPluginObject> newHookPluginObjects = new ArrayList<>();
            for (HookPluginObject hookPluginObject : hookPluginObjects) {
                if (!hookPluginObject.getPluginId().equals(pluginID)) {
                    newHookPluginObjects.add(hookPluginObject);
                }
            }
            HookServices.hookMap.put(hook_key, newHookPluginObjects);
        });
        //Remove the plugin ID from the plugins map
        HookServices.pluginsMap.remove(pluginID);
    }
}